package org.onionframework.persistence.dao.dynamic;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.onionframework.core.config.SystemConfig;
import org.onionframework.persistence.dao.escape.DatabaseEscapeUtils;
import org.onionframework.persistence.query.MatchType;
import org.onionframework.persistence.query.OnionQuery;
import org.onionframework.persistence.query.PropertyFilter;

public class NamedQueryBuilder {

	private StatementTemplateSupport statementTemplateSupport;
	private Query query;
	private Session session;
	
	public NamedQueryBuilder(StatementTemplateSupport statementTemplateSupport,Session session) {
		this.statementTemplateSupport = statementTemplateSupport;
		this.session = session;
	}
	
	public Query build(OnionQuery onionQuery) {
		StatementTemplate template = this.statementTemplateSupport.getStatementTemplate(onionQuery.getName());
		
		if(template == null){
			throw new RuntimeException("找不到查询NamedQuery定义：" + onionQuery.getName());
		}
		
		if(template.getType() == StatementType.HQL){
			this.query = this.session.createQuery(template.processTemplate(this.toMap(onionQuery)));
		}else{
			SqlStatementTemplate sqlStatementTemplate = (SqlStatementTemplate)template;
			this.query = this.session.createSQLQuery(sqlStatementTemplate.processTemplate(this.toMap(onionQuery)));
			if(SqlStatementTemplate.RESULT_TYPE_MAP.equals(sqlStatementTemplate.getResultType()))
				this.query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		}
		
		for(Map.Entry<String, Object> param : this.toMap(onionQuery).entrySet()){
			if(param.getValue() instanceof Object[])
				this.query.setParameterList(param.getKey(), (Object[])param.getValue());
			else if(param.getValue() instanceof Collection)
				this.query.setParameterList(param.getKey(), (Collection<?>)param.getValue());
			else
				this.query.setParameter(param.getKey(), param.getValue());
		}

		query.setFirstResult(onionQuery.getStart());
		
		if(onionQuery.getLimit() > 0)
			query.setMaxResults(onionQuery.getLimit());
		
		return query;
	}
	
	public Query buildCount(OnionQuery onionQuery){
		
		StatementTemplate countTemplate = this.statementTemplateSupport.getStatementTemplate(onionQuery.getName() + "-count");
		
		if(countTemplate != null){
			if(countTemplate.getType() == StatementType.HQL){
				this.query = this.session.createQuery(countTemplate.processTemplate(this.toMap(onionQuery)));
			}else{
				this.query = this.session.createSQLQuery(countTemplate.processTemplate(this.toMap(onionQuery)));
			}
		}else{
			StatementTemplate template = this.statementTemplateSupport.getStatementTemplate(onionQuery.getName());
			
			if(template == null){
				throw new RuntimeException("找不到查询NamedQuery定义：" + onionQuery.getName());
			}
			
			if(template.getType() == StatementType.HQL){
				String hql = template.processTemplate(this.toMap(onionQuery));
				String countHQL = " select count (*) "
						+ HqlUtils.removeSelect(HqlUtils.removeOrders(hql));
				this.query = this.session.createQuery(countHQL);
			}else{
				String statement = template.processTemplate(this.toMap(onionQuery));
				statement = "select count(1) from (" + statement + ")";
				this.query = this.session.createSQLQuery(statement);
			}
		}
		
		for(Map.Entry<String, Object> param : this.toMap(onionQuery).entrySet()){
			if(param.getValue() instanceof Object[])
				this.query.setParameterList(param.getKey(), (Object[])param.getValue());
			else if(param.getValue() instanceof Collection)
				this.query.setParameterList(param.getKey(), (Collection<?>)param.getValue());
			else
				this.query.setParameter(param.getKey(), param.getValue());
		}


		return query;
	}
	
	public Map<String, Object> toMap(OnionQuery query) {
		Map<String, Object> map = new HashMap<String, Object>();
		Object paramValue = null;
		String paramName;

		for (PropertyFilter propertyFilter : query.list()) {
			paramName = propertyFilter.getOriginalName();

			if (StringUtils.isBlank(paramName))
				paramName = propertyFilter.getPropertyName();

			if (paramName.endsWith("_" + MatchType.LIKE.name().toLowerCase())){
				if(SystemConfig.Config.getBoolean("onion.core.auto_escape_like",true))
					paramValue = "%" + DatabaseEscapeUtils.escape((String)propertyFilter.getPropertyValue()) + "%";
				else
					paramValue = "%" + propertyFilter.getPropertyValue() + "%";
			}
			else if(propertyFilter.getPropertyValue() instanceof OnionQuery){
				paramValue = this.toMap((OnionQuery)propertyFilter.getPropertyValue());
			}else
				paramValue = propertyFilter.getPropertyValue();
			
			map.put(paramName, paramValue);
		}

		return map;
	}
}
